using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace SilkSharp.src
{
	/*
	 * Variable order MA filter.
	 *
	 * @author Jing Dai
	 * @author Dingxin Xu
	 */
	public class Silk_MA
	{
		/*
		 * Variable order MA filter.
		 * @param _in input signal.
		 * @param in_offset offset of valid data.
		 * @param B MA coefficients, Q13 [order+1].
		 * @param S state vector [order].
		 * @param _out output signal.
		 * @param out_offset offset of valid data.
		 * @param len signal length.
		 * @param order filter order.
		 */
		static void SKP_Silk_MA(
				short[] _in,            /* I:   input signal                                */
				int in_offset,
				short[] B,             /* I:   MA coefficients, Q13 [order+1]              */
				int[] S,             /* I/O: state vector [order]                        */
				short[] _out,           /* O:   output signal                               */
				int out_offset,
				int len,            /* I:   signal length                               */
				int order           /* I:   filter order                                */
		)
		{
			int k, d, in16;
			int out32;

			for( k = 0; k < len; k++ )
			{
				in16 = _in[ in_offset + k ];
				out32 = Silk_macros.SKP_SMLABB( S[ 0 ], in16, B[ 0 ] );
				out32 = Silk_SigProc_FIX.SKP_RSHIFT_ROUND( out32, 13 );

				for( d = 1; d < order; d++ )
				{
					S[ d - 1 ] = Silk_macros.SKP_SMLABB( S[ d ], in16, B[ d ] );
				}
				S[ order - 1 ] = Silk_macros.SKP_SMULBB( in16, B[ order ] );

				/* Limit */
				_out[ out_offset + k ] = (short)Silk_SigProc_FIX.SKP_SAT16( out32 );
			}
		}

		/*
		 * Variable order MA prediction error filter.
		 * @param _in Input signal.
		 * @param in_offset offset of valid data.
		 * @param B MA prediction coefficients, Q12 [order].
		 * @param B_offset 
		 * @param S State vector [order].
		 * @param _out Output signal.
		 * @param out_offset offset of valid data.
		 * @param len Signal length.
		 * @param order Filter order.
		 */
		public static void SKP_Silk_MA_Prediction(
				short[] _in,            /* I:   Input signal                                */
				int in_offset,
				short[] B,             /* I:   MA prediction coefficients, Q12 [order]     */
				int B_offset,
				int[] S,             /* I/O: State vector [order]                        */
				short[] _out,           /* O:   Output signal                               */
				int out_offset,
				int len,            /* I:   Signal length                               */
				int order           /* I:   Filter order                                */
			)
		{
			int k, d, in16;
			int out32;

			for( k = 0; k < len; k++ )
			{
				in16 = _in[ in_offset + k ];
				out32 = ( in16 << 12 ) - S[ 0 ];
				out32 = Silk_SigProc_FIX.SKP_RSHIFT_ROUND( out32, 12 );

				for( d = 0; d < order - 1; d++ )
				{
					S[ d ] = Silk_SigProc_FIX.SKP_SMLABB_ovflw( S[ d + 1 ], in16, B[ B_offset + d ] );
				}
				S[ order - 1 ] = Silk_macros.SKP_SMULBB( in16, B[ B_offset + order - 1 ] );

				/* Limit */
				_out[ out_offset + k ] = (short)Silk_SigProc_FIX.SKP_SAT16( out32 );
			}
		}

		/*
		 * 
		 * @param _in input signal.
		 * @param in_offset offset of valid data.
		 * @param B MA prediction coefficients, Q13 [order].
		 * @param S state vector [order].
		 * @param _out output signal.
		 * @param out_offset offset of valid data.
		 * @param len signal length.
		 * @param order filter order.
		 */
		static void SKP_Silk_MA_Prediction_Q13(
				short[] _in,            /* I:   input signal                                */
				int in_offset,
				short[] B,             /* I:   MA prediction coefficients, Q13 [order]     */
				int[] S,             /* I/O: state vector [order]                        */
				short[] _out,           /* O:   output signal                               */
				int out_offset,
				int len,            /* I:   signal length                               */
				int order           /* I:   filter order                                */
			)
		{
			int k, d, in16;
			int out32;
			for( k = 0; k < len; k++ )
			{
				in16 = _in[ in_offset + k ];
				out32 = ( in16 << 13 ) - S[ 0 ];
				out32 = Silk_SigProc_FIX.SKP_RSHIFT_ROUND( out32, 13 );

				for( d = 0; d < order - 1; d++ )
				{
					S[ d ] = Silk_macros.SKP_SMLABB( S[ d + 1 ], in16, B[ d ] );
				}
				S[ order - 1 ] = Silk_macros.SKP_SMULBB( in16, B[ order - 1 ] );

				/* Limit */
				_out[ out_offset + k ] = (short)Silk_SigProc_FIX.SKP_SAT16( out32 );
			}
		}

		/*
		 * 
		 * @param _in Input signal.
		 * @param in_offset offset of valid data.
		 * @param B MA prediction coefficients, Q12 [order].
		 * @param S State vector [order].
		 * @param _out Output signal.
		 * @param out_offset offset of valid data.
		 * @param len Signal length.
		 * @param Order Filter order.
		 */
		static void SKP_Silk_LPC_analysis_filter(
				short[] _in,            /* I:   Input signal                                */
				int in_offset,
				short[] B,             /* I:   MA prediction coefficients, Q12 [order]     */
				short[] S,             /* I/O: State vector [order]                        */
				short[] _out,           /* O:   Output signal                               */
				int out_offset,
				int len,            /* I:   Signal length                               */
				int Order           /* I:   Filter order                                */
			)
		{
			int k, j, idx, Order_half = ( Order >> 1 );
			int out32_Q12, out32;
			short SA, SB;
			/* Order must be even */
			Silk_typedef.SKP_assert( 2 * Order_half == Order );

			/* S[] values are _in Q0 */
			for( k = 0; k < len; k++ )
			{
				SA = S[ 0 ];
				out32_Q12 = 0;
				for( j = 0; j < ( Order_half - 1 ); j++ )
				{
					idx = Silk_macros.SKP_SMULBB( 2, j ) + 1;
					/* Multiply-add two prediction coefficients for each loop */
					SB = S[ idx ];
					S[ idx ] = SA;
					out32_Q12 = Silk_macros.SKP_SMLABB( out32_Q12, SA, B[ idx - 1 ] );
					out32_Q12 = Silk_macros.SKP_SMLABB( out32_Q12, SB, B[ idx ] );
					SA = S[ idx + 1 ];
					S[ idx + 1 ] = SB;
				}

				/* Unrolled loop: epilog */
				SB = S[ Order - 1 ];
				S[ Order - 1 ] = SA;
				out32_Q12 = Silk_macros.SKP_SMLABB( out32_Q12, SA, B[ Order - 2 ] );
				out32_Q12 = Silk_macros.SKP_SMLABB( out32_Q12, SB, B[ Order - 1 ] );

				/* Subtract prediction */
				out32_Q12 = Silk_macros.SKP_SUB_SAT32( ( (int)_in[ in_offset + k ] << 12 ), out32_Q12 );

				/* Scale to Q0 */
				out32 = Silk_SigProc_FIX.SKP_RSHIFT_ROUND( out32_Q12, 12 );

				/* Saturate output */
				_out[ out_offset + k ] = (short)Silk_SigProc_FIX.SKP_SAT16( out32 );

				/* Move input line */
				S[ 0 ] = _in[ in_offset + k ];
			}
		}
	}
}
